﻿using Newtonsoft.Json;
using ProtoBuf;
using Stee.CAP8.MessageBus.Interface.Models;
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Text;

namespace Stee.CAP8.MessageBus.Sagas.Core.Common
{
    public static class DataConvert
    {

        public static T ToObject<T>(this byte[] bytes)
        {
            if (bytes == null)
                return default;
            var jsonString = Encoding.UTF8.GetString(bytes);
            if (string.IsNullOrWhiteSpace(jsonString))
                return default;
            try
            {
                var obj = JsonConvert.DeserializeObject<T>(jsonString);
                return obj;
            }
            catch (Exception ex)
            {
                return default;
            }
        }

        public static byte[] ObjToBytes(this object obj)
        {
            if (obj == null) return null;
            var jsonstring = JsonConvert.SerializeObject(obj);
            var bytes = Encoding.UTF8.GetBytes(jsonstring);
            return bytes;
        }
    }


    public static class SerializerUtilitys
    {
        public static byte[] Serialize(object instance)
        {
            using var stream = new MemoryStream();
            Serializer.Serialize(stream, instance);
            return stream.ToArray();
        }

        public static object Deserialize(byte[] data, Type type)
        {
            if (data == null)
                return null;
            using var stream = new MemoryStream(data);
            return Serializer.Deserialize(type, stream);
        }

        public static T Deserialize<T>(byte[] data)
        {
            if (data == null)
                return default;
            using var stream = new MemoryStream(data);
            return Serializer.Deserialize<T>(stream);
        }
    }

    public static class CommandMessageExtensions
    {
        public static object GetObject(this CommandMessage message)
        {
            var typeName = message.GetHeaderByKey("typeName");
            if (typeName == null) 
            {
                throw new SerializationException("没有设置CommandMessage中Data的类型，无法进行Deserialize");
            }
            var type = Type.GetType(typeName.ToString());

            var obj = SerializerUtilitys.Deserialize(message.Data, type);
            return obj;
        }
        public static T GetObject<T>(this CommandMessage message)
        {
            var typeName = message.GetHeaderByKey("typeName");
            if (typeName == null) 
            {
                throw new SerializationException("没有设置CommandMessage中Data的类型，无法进行Deserialize");
            }
            var type = Type.GetType(typeName.ToString());

            if (type != typeof(T)) 
            {
                throw new SerializationException($"CommandMessage中Data的类型不是{typeof(T).FullName}，无法进行Deserialize");
            }

            var obj = SerializerUtilitys.Deserialize<T>(message.Data);
            return obj;
        }
        public static void SetObject(this CommandMessage message, object obj)
        {
            var typeName = Shims.GetSimpleAssemblyQualifiedName(obj.GetType()); 
            message.AddHeader("typeName", typeName);
            message.Data = SerializerUtilitys.Serialize(obj);

        }

        public static void SetObjectType<T>(this CommandMessage message, T obj) 
        {
            var typeName = Shims.GetSimpleAssemblyQualifiedName(typeof(T));
            message.AddHeader("typeName", typeName);
            message.Data = SerializerUtilitys.Serialize(obj);

        }
    }


    static class Shims
    {
        public static string GetSimpleAssemblyQualifiedName(Type type)
        {
            var simpleAssemblyQualifiedName = BuildSimpleAssemblyQualifiedName(type, new StringBuilder()).ToString();

            // type lookups apparently accept "mscorlib" as an alias for System.Private.CoreLib, so we can make the "simple assembly-qualified type name" consistent across platforms like this
            return simpleAssemblyQualifiedName.Replace("System.Private.CoreLib", "mscorlib");
        }

        static StringBuilder BuildSimpleAssemblyQualifiedName(Type type, StringBuilder sb)
        {
            if (!type.IsGenericType)
            {
                sb.Append($"{type.FullName}, {type.Assembly.GetName().Name}");
                return sb;
            }

            if (!type.IsConstructedGenericType)
            {
                return sb;
            }

            var fullName = type.FullName ?? "???";
            var requiredPosition = fullName.IndexOf("[", StringComparison.Ordinal);
            var name = fullName.Substring(0, requiredPosition);
            sb.Append($"{name}[");

            var arguments = type.GetGenericArguments();
            for (var i = 0; i < arguments.Length; i++)
            {
                sb.Append(i == 0 ? "[" : ", [");
                BuildSimpleAssemblyQualifiedName(arguments[i], sb);
                sb.Append("]");
            }

            sb.Append($"], {type.Assembly.GetName().Name}");

            return sb;
        }
    }
}
